package com.bridgeintelligent.tag.engine.es.query.condition;

import com.bridgeintelligent.tag.engine.api.Filter;
import com.bridgeintelligent.tag.engine.api.FilterGroup;
import com.bridgeintelligent.tag.engine.api.FilterTypeConfig;
import com.bridgeintelligent.tag.engine.es.query.EsConditionBuilder;
import org.apache.commons.collections.CollectionUtils;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;

import java.util.*;

/**
 * Created by JackCheung on 2019/10/15 5:21 AM.
 */
public class BreadthFirstQueue {

    /**
     * filterGroup转换为QueryBuilder
     *
     * @param filterGroup filterGroup
     * @return QueryBuilder
     */
    public static QueryBuilder handler(FilterGroup filterGroup) {
        Queue<FilterGroup> queue = new LinkedList<>();
        FilterGroup children;
        //往队列尾部插入filterGroup
        queue.offer(filterGroup);
        //创建BoolQueryBuilder
        BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();
        //得到父布尔关系类型
        String parentType = filterGroup.getSymbol();
        //如果是and关系类型，把过滤条件用and连接
        if (FilterTypeConfig.AND.equals(filterGroup.getSymbol())) {
            andHandler(queryBuilder, filterGroup);
        } else {//否则用or连接过滤条件
            orHandler(queryBuilder, filterGroup);
        }
        List<BoolQueryBuilder> list = new ArrayList<>();
        Map<Integer, String> signMap = new HashMap<>(16);
        int sign = 0;
        //关联所有过滤条件
        while (!queue.isEmpty()) {
            children = queue.poll();
            String childrenSymbol = children.getSymbol();
            if (CollectionUtils.isNotEmpty(children.getChildren())) {
                BoolQueryBuilder childBuilder = QueryBuilders.boolQuery();
                for (FilterGroup filterGroupChild : children.getChildren()) {
                    if (FilterTypeConfig.OR.equals(filterGroupChild.getSymbol())) {
                        andHandler(childBuilder, filterGroupChild);
                    } else {
                        orHandler(childBuilder, filterGroupChild);
                    }
                    queue.offer(filterGroupChild);
                }
                signMap.put(sign, childrenSymbol);
                list.add(childBuilder);
                sign++;
            }
        }
        return createResult(list, signMap, parentType, queryBuilder);
    }

    /**
     * 创建QueryBuilder结果
     *
     * @param list         list
     * @param signMap      signMap
     * @param parentType   parentType
     * @param queryBuilder queryBuilder
     * @return QueryBuilder
     */
    private static QueryBuilder createResult(List<BoolQueryBuilder> list, Map<Integer, String> signMap, String parentType, BoolQueryBuilder queryBuilder) {
        if (CollectionUtils.isNotEmpty(list)) {
            List<BoolQueryBuilder> nestedList = new ArrayList<>();
            int size = list.size();
            if (size > 1) {
                for (int i = size - 1; i >= 1; i--) {
                    BoolQueryBuilder b = QueryBuilders.boolQuery();
                    String type = signMap.get(i);
                    BoolQueryBuilder tempBuilder;
                    if (CollectionUtils.isEmpty(nestedList)) {
                        tempBuilder = list.get(i);
                    } else {
                        tempBuilder = nestedList.get(0);
                    }
                    if (type.equals(FilterTypeConfig.AND)) {
                        b.must(tempBuilder)
                         .must(list.get(i - 1));
                    } else {
                        b.should(tempBuilder)
                         .should(list.get(i - 1));
                    }
                    nestedList.clear();
                    nestedList.add(b);
                }
                fetchFinal(nestedList, queryBuilder, parentType);
            } else {//无嵌套
                fetchFinal(list, queryBuilder, parentType);
            }
        }
        return queryBuilder;
    }

    /**
     * 无嵌套关联
     *
     * @param response     response
     * @param queryBuilder queryBuilder
     * @param parentType   parentType
     */
    private static void fetchFinal(List<BoolQueryBuilder> response, BoolQueryBuilder queryBuilder, String parentType) {
        //and关系
        if (FilterTypeConfig.AND.equals(parentType)) {
            queryBuilder.must(response.get(0));
        } else {//or关系
            queryBuilder.should(response.get(0));
        }
    }

    /**
     * 用or关联过滤条件
     *
     * @param queryBuilder queryBuilder
     * @param filterGroup  filterGroup
     */
    private static void orHandler(BoolQueryBuilder queryBuilder, FilterGroup filterGroup) {
        for (Filter filter : filterGroup.getFilters()) {
            queryBuilder.should(EsConditionBuilder.choseFilterType(filter));
        }
    }

    /**
     * 用and关联过滤条件
     *
     * @param queryBuilder queryBuilder
     * @param filterGroup  filterGroup
     */
    private static void andHandler(BoolQueryBuilder queryBuilder, FilterGroup filterGroup) {
        for (Filter filter : filterGroup.getFilters()) {
            queryBuilder.must(EsConditionBuilder.choseFilterType(filter));
        }
    }

    public static QueryBuilder transform(FilterGroup filterGroup) {
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        // 得到布尔关系类型
        String type = filterGroup.getSymbol();
        // 获取当前层级的筛选条件
        List<Filter> filters = filterGroup.getFilters();
        // 获取所有子层级
        List<FilterGroup> children = filterGroup.getChildren();
        // 先拼接当前层级的条件
        // 载拼接字层级的条件
        if (FilterTypeConfig.AND.equals(type)) {
            if (filters != null && filters.size() > 0) {
                filters.forEach(filter -> boolQueryBuilder.must(EsConditionBuilder.choseFilterType(filter)));
            }
            if (children != null && children.size() > 0) {
                children.forEach(child -> boolQueryBuilder.must(transform(child)));
            }
        } else {
            if (filters != null && filters.size() > 0) {
                filters.forEach(filter -> boolQueryBuilder.should(EsConditionBuilder.choseFilterType(filter)));
            }
            if (children != null && children.size() > 0) {
                children.forEach(child -> boolQueryBuilder.should(transform(child)));
            }
        }
        return boolQueryBuilder;
    }
}
